package jp.co.kayo.android.sample.mediaplayer;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.List;

import android.text.TextUtils;

public class RelayService implements Runnable {
    ServerSocket mSocket;
    Thread mThread;
    boolean bStop;
    File mCacheFile = null;
    private final int BUF_SIZE = 1024*24;

    public RelayService(File file) {
        try {
            mCacheFile = file;
            mSocket = new ServerSocket(0, 0,
                    InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
            mSocket.setSoTimeout(5000);
        } catch (UnknownHostException e) {
            Log.e("get InetAddress", e);
        } catch (IOException e) {
            Log.e("create serversocket", e);
        }
    }

    public int getPort() {
        return mSocket.getLocalPort();
    }

    public void start() {
        mThread = new Thread(this);
        mThread.start();
    }

    public void stop() {
        bStop = true;
        try {
            mThread.join(5000);
        } catch (InterruptedException e) {
            Log.e("thread join", e);
        }
    }

    @Override
    public void run() {
        while (!bStop) {
            try {
                final Socket client = mSocket.accept();
                if (client == null) {
                    continue;
                }
                Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        procMain(client);
                    }
                };
                new Thread(r).start();
            } catch (SocketTimeoutException e) {
            } catch (IOException e) {
                Log.e("io error", e);
            }
        }
    }

    private void procMain(Socket client) {
        InputStream remoetInputStream = null;
        InputStream clientInputStream = null;
        BufferedOutputStream cacheOutputStream = null;
        String line;
        String uri;

        Boolean streamComplete = false;

        try {
            clientInputStream = client.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    clientInputStream));
            line = reader.readLine();
            
            if (line == null) {
                Log.d("url nothing");
                return;
            }
            
            StringTokenizer st = new StringTokenizer(line);
            String s = st.nextToken();
            uri = st.nextToken().substring(1);

            if (mCacheFile.exists()) {
                mCacheFile.delete();
            }

            HttpURLConnection connection = (HttpURLConnection) (new URL(uri))
                    .openConnection();

            connection.setDoOutput(true);
            connection.connect();

            String status = "HTTP/1.1 " + connection.getResponseCode() + " "
                    + connection.getResponseMessage() + "¥n";
            client.getOutputStream().write(status.getBytes());

            for (Entry<String, List<String>> e : connection.getHeaderFields()
                    .entrySet()) {
                if (e.getKey().equals("Accept-Ranges")) {
                    continue;
                }
                String header = e.getKey() + ": "
                        + TextUtils.join(";", e.getValue()) + "¥n";
                client.getOutputStream().write(header.getBytes());
            }
            client.getOutputStream().write("¥n".getBytes());

            remoetInputStream = connection.getInputStream();
            cacheOutputStream = new BufferedOutputStream(new FileOutputStream(
                    mCacheFile));

            byte[] buf = new byte[BUF_SIZE];
            int readSize;
            while ((readSize = remoetInputStream.read(buf)) != -1 && !bStop) {
                if (cacheOutputStream != null) {
                    try {
                        cacheOutputStream.write(buf, 0, readSize);
                    } catch (IOException e) {
                        Log.e("write cachefile", e);
                        try {
                            cacheOutputStream.close();
                        } catch (IOException ex) {
                        }
                        cacheOutputStream = null;
                    }
                }
                client.getOutputStream().write(buf, 0, readSize);
            }

            streamComplete = (cacheOutputStream != null && !bStop);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (remoetInputStream != null) {
                try {
                    remoetInputStream.close();
                } catch (IOException e) {
                }
            }
            if (clientInputStream != null) {
                try {
                    clientInputStream.close();
                } catch (IOException e) {
                }
            }
            if (cacheOutputStream != null) {
                try {
                    cacheOutputStream.close();
                } catch (IOException e) {
                }
            }
            try {
                client.close();
            } catch (IOException e) {
            }
        }

        if (!streamComplete && mCacheFile != null && mCacheFile.exists()) {
            mCacheFile.delete();
        }
    }

}
